package com.terra.ns.imp.system.service.impl

import com.terra.ns.imp.common.redis.utils.RedisUtils
import com.terra.ns.imp.common.vo.request.ByCodeRequest
import com.terra.ns.imp.system.constant.SystemCacheKeys
import com.terra.ns.imp.system.constant.SystemConstants
import com.terra.ns.imp.system.constant.SystemErrorCode
import com.terra.ns.imp.system.constant.enums.DeptSyncTypeEnum
import com.terra.ns.imp.system.convert.DepartmentConvert
import com.terra.ns.imp.system.entity.StDepartment
import com.terra.ns.imp.system.exception.SystemServiceException
import com.terra.ns.imp.system.mapper.StDepartmentMapper
import com.terra.ns.imp.system.mapper.StDeptDingExtMapper
import com.terra.ns.imp.system.mapper.StUserDeptRelMapper
import com.terra.ns.imp.system.service.IDeptSyncService
import com.terra.ns.imp.system.service.IStDepartmentService
import com.terra.ns.imp.system.service.helper.StDepartmentHelper
import com.terra.ns.imp.system.vo.base.DeptBaseRequest
import com.terra.ns.imp.system.vo.base.DeptBaseResponse
import com.terra.ns.imp.system.vo.request.EditDeptRequest
import com.terra.ns.imp.system.vo.request.QueryDeptTreeRequest
import com.terra.ns.imp.system.vo.response.DeptTreeResponse
import com.terra.ns.imp.system.vo.response.ding.SyncDeptRateResponse
import jakarta.annotation.Resource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.concurrent.Executors

/**
@author qins
@date 2023/6/6
@desc
 */
@Service
class StDepartmentServiceImpl: IStDepartmentService {

    @Resource
    @Qualifier("dingDeptSyncService")
    private lateinit var dingDeptSyncService: IDeptSyncService

    @Autowired
    private lateinit var departmentMapper: StDepartmentMapper

    @Autowired
    private lateinit var userDeptRelMapper: StUserDeptRelMapper

    @Autowired
    private lateinit var deptDingExtMapper: StDeptDingExtMapper

    @Autowired
    private lateinit var departmentHelper: StDepartmentHelper

    private val syncDingDeptThread = Executors.newSingleThreadExecutor()

    @Transactional
    override fun addDept(request: DeptBaseRequest): String {
        checkDept(request.parentCode, null, request.name)
        val insertDept = DepartmentConvert.convert(request)
        // 生成部门code，规则4位
        val deptCode = departmentHelper.generateDeptCode()
        insertDept.code = deptCode
        // 计算部门nodePath
        insertDept.nodePath = departmentHelper.computeNodePath(request.parentCode, deptCode)
        // 获取顶级部门code
        insertDept.topNodeCode = departmentHelper.computeTopCode(request.parentCode)
        departmentMapper.insert(insertDept)
        return deptCode
    }

    @Transactional
    override fun updateDept(request: EditDeptRequest) {
        val dept = departmentMapper.selectByCode(request.code) ?: throw SystemServiceException(SystemErrorCode.DEPT_NOT_EXIST)
        checkDept(request.parentCode, request.code, request.name)
        val updateDept = DepartmentConvert.convert(request)
        updateDept.topNodeCode = dept.topNodeCode
        updateDept.nodePath = dept.nodePath
        //根部门不允许修改名称---仅支持跟随企业名称一并修改
        if (dept.parentCode == SystemConstants.TOP_DEPT_PARENT_CODE && request.name != dept.name) {
                throw SystemServiceException(SystemErrorCode.TOP_DEPT_NAME_CAN_NOT_MODIFY)
        }
        // 改变了父部门
        if (dept.parentCode != request.parentCode) {
            // 计算部门nodePath
            updateDept.nodePath = departmentHelper.computeNodePath(request.parentCode, request.code)
            // 获取顶级部门code
            updateDept.topNodeCode = departmentHelper.computeTopCode(request.parentCode)
            // 计算子部门的nodePath
            val updateChildrenList = computeChildrenNodePath(dept.nodePath!!, updateDept.nodePath!!)
            if (updateChildrenList.isNotEmpty()) {
                val updateList = updateChildrenList.map {
                    val update = StDepartment()
                    update.nodePath = it.nodePath
                    update.id = it.id
                    update
                }
                departmentMapper.batchUpdateById(updateList)
            }
        }
        departmentMapper.updateAllFieldByCode(updateDept, request.code)
    }

    @Transactional
    override fun deleteDept(request: ByCodeRequest) {
        checkDeptExist(request.code)!!
        //部门名下有子部门或成员不允许删除
        normalDelete(request.code)
//        eventPublisher.publishDeptEvent(dept.code!!,tenantId,TeDeptEventEnum.DEL_DEPT)
    }

    override fun selectDetail(request: ByCodeRequest): DeptBaseResponse {
        val dept = departmentMapper.selectByCode(request.code)
            ?: throw SystemServiceException(SystemErrorCode.DEPT_NOT_EXIST)
        return DepartmentConvert.convertToBaseResponse(dept)
    }

    override fun selectDeptTree(request: QueryDeptTreeRequest): List<DeptTreeResponse> {
        // 处理根据name搜索只返回匹配部门的所有上级
        if (!request.name.isNullOrBlank()) {
            return selectDeptTreeByName(request.name!!)
        }
        var topCode = request.code
        val deptList: List<StDepartment>
        if (!topCode.isNullOrBlank()) {
            val dept = departmentMapper.selectOneByCode(topCode)
                ?: throw SystemServiceException(SystemErrorCode.DEPT_NOT_EXIST)
            // 查询该部门下的所有子部门
            deptList = departmentMapper.selectByLikeNodePath(dept.nodePath!!)
        } else {
            // 查询所有部门
            deptList = departmentMapper.selectAllDept()
            topCode = SystemConstants.TOP_DEPT_PARENT_CODE
        }
        // 构建部门树
        return buildDeptTree(topCode, deptList.map {
            DepartmentConvert.convertDeptTreeResponse(
                it,
            )
        })
    }

    /**
     * 计算子部门的nodePath
     */
    private fun computeChildrenNodePath(oldNodePath: String, newNodePath: String): List<StDepartment> {
        //查询子部门，nodePath包含
        val childrenDeptList = departmentMapper.selectByLikeNodePath(oldNodePath)
        if (childrenDeptList.isEmpty()) return emptyList()
        childrenDeptList.forEach {
            it.nodePath = it.nodePath?.replace(oldNodePath, newNodePath)
        }
        return childrenDeptList
    }

    private fun checkDept(parentCode: String, code: String?, name: String) {
        checkDeptExist(code)
        checkNameUnique(parentCode, code, name)
    }

    private fun checkDeptExist(code: String?): StDepartment? {
        if (code == null) return null
        return departmentMapper.selectByCode(code) ?: throw SystemServiceException(SystemErrorCode.DEPT_NOT_EXIST)
    }

    /**
     * 检查同一级部门下不能有同名部门
     */
    private fun checkNameUnique(parentCode: String, code: String?, name: String) {
        if (name.isBlank()) throw SystemServiceException(SystemErrorCode.DEPT_NAME_ERROR)
        val deptList = departmentMapper.selectByName(name)
        if (deptList.isEmpty()) return
        deptList.forEach foreachParentCode@{
            if (parentCode != it.parentCode) {
                return@foreachParentCode
            }
            // 部门编码为空时，已有该名称的部门 || 部门编码不为空，但不是当前编码，表示已有该名称部门
            if (code == null || code != it.code) throw SystemServiceException(SystemErrorCode.LEVEL_DEPT_NAME_EXIST)
        }
    }

    private fun normalDelete(deptCode: String) {
        val childrenList = departmentMapper.selectByParentCode(deptCode)
        if (childrenList.isNotEmpty()) {
            throw SystemServiceException(SystemErrorCode.DEL_HAS_CHILDREN_DEPT)
        }
        if (userDeptRelMapper.selectListByDeptCode(deptCode).isNotEmpty()) {
            throw SystemServiceException(SystemErrorCode.DEL_HAS_USER_DEPT)
        }
        departmentMapper.deleteByCode(deptCode)
        // 删除钉钉部门扩展信息
        deptDingExtMapper.deleteByDeptCode(deptCode)
    }

    override fun selectDeptTreeByName(name: String): List<DeptTreeResponse> {
        val deptList = departmentMapper.selectByLikeName(name)
        if (deptList.isEmpty()) return emptyList()
        val allParentCode = deptList.mapNotNull(StDepartment::code).toHashSet()
        deptList.forEach {
            val parentCode = it.nodePath?.split("-")?.filterNot { code -> code.isBlank() }
            if (!parentCode.isNullOrEmpty()) allParentCode.addAll(parentCode)
        }
        if (allParentCode.isNotEmpty()) {
            val resultDeptList = departmentMapper.selectListByCodes(allParentCode.toList())
            // 构建部门树
            return buildDeptTree(SystemConstants.TOP_DEPT_PARENT_CODE, resultDeptList.map {
                DepartmentConvert.convertDeptTreeResponse(
                    it,
                )
            })
        }
        return emptyList()
    }

    override fun syncDingDept() {
        dingDeptSyncService.initSyncDeptCache()
        syncDingDeptThread.submit{ dingDeptSyncService.doSyncDept(DeptSyncTypeEnum.FULL) }
    }

    override fun getSyncDeptRate(): SyncDeptRateResponse {
        val rate = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_RATE_KEY, 0)
        val deptTotal = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_TOTAL_KEY, 0)
        val successCount = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_SUCCESS_COUNT_KEY, 0)
        val newDeptCount = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_NEW_DEPT_COUNT_KEY, 0)
        val newUserCount = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_NEW_USER_COUNT_KEY, 0)
        val coverUserCount = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_COVER_USER_COUNT_KEY, 0)
        val skipUserCount = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_SKIP_USER_COUNT_KEY, 0)
        val result = RedisUtils.getOrDefault(SystemCacheKeys.IMPORT_DEPT_RESULT_DESC_KEY, "")
        return SyncDeptRateResponse(
            rate,
            deptTotal,
            successCount,
            newDeptCount,
            newUserCount,
            coverUserCount,
            skipUserCount,
            result
        )
    }

    /**
     * 构建部门树结构
     */
    private fun buildDeptTree(topCode: String, deptList: List<DeptTreeResponse>): List<DeptTreeResponse> {
        val parentGroup = deptList.groupBy { it.parentCode ?: SystemConstants.TOP_DEPT_PARENT_CODE } //按照关联的父部门分组
        val topDeptList = if (topCode != SystemConstants.TOP_DEPT_PARENT_CODE) {
            deptList.filter { it.code == topCode }
        } else {
            parentGroup[SystemConstants.TOP_DEPT_PARENT_CODE] ?: emptyList()
        }
        topDeptList.forEach {
            forChildren(parentGroup, it)
        }
        return topDeptList
    }

    /**
     * 递归设置children
     */
    private fun forChildren(parentGroup: Map<String, List<DeptTreeResponse>>, currDept: DeptTreeResponse) {
        val children = parentGroup[currDept.code]
        currDept.children = children
        if (children == null) {
            return
        }
        for (child in children) {
            forChildren(parentGroup, child)
        }
    }
}